
/* GCSx
** TILESETBROWSE.CPP
**
** Tileset browsing
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#include "all.h"


// Tool panel- Mode

int TileSetBrowseToolsMode::event(int hasFocus, const SDL_Event* event) { start_func
    if (event->type == SDL_COMMAND) {
        // Choose mode
        if (((event->user.code == MODE_EDITTILES) ||
             (event->user.code == MODE_EDITCOLLISION) ||
             (event->user.code == MODE_EDITASSIGN)) && (!font)) {

            *modePtr = event->user.code;
            updateTools();
            
            // Change mode (*mode aleady updated)
            myBrowse->reloadTileStats();
            return 1;
        }
    }

    return Dialog::event(hasFocus, event);
}

void TileSetBrowseToolsMode::updateTools() { start_func
    if (!font) {
        for (int pos = 0; pos <= 2; ++pos) {
            WRadioButton* w = dynamic_cast<WRadioButton*>(findWidget(ID_MODE, pos));
            w->load();
            if (w->getValue() != MODE_EDITTILES) {
                if (*hasMapsPtr) {
                    w->enable();
                }
                else {
                    w->disable();
                }
            }
        }
    }
}

void TileSetBrowseToolsMode::childModified(Window* modified) { start_func
    Dialog::childModified(modified);

    // Change mode (*mode aleady updated)
    myBrowse->reloadTileStats();
}

int TileSetBrowseToolsMode::wantsToBeDeleted() const { start_func
    return 1;
}

FrameWindow* TileSetBrowseToolsMode::createWindowed() { start_func
    // Prevent duplication
    if (myFrame) {
        return myFrame;
    }

    // We remember the frame pointer even though it'll delete itself
    myFrame = new FrameWindow("Modes", FrameWindow::RESIZING_SNAP, FrameWindow::FRAMETYPE_DIALOG, this, FrameWindow::TITLEBAR_TOOL, 0);
    return myFrame;
}

Window::WindowSort TileSetBrowseToolsMode::windowSort() const { start_func
    return WINDOWSORT_ONTOP;
}

TileSetBrowseToolsMode::TileSetBrowseToolsMode(TileSetBrowse* tBrowseArea, int isFont, int* mode, const int* hasMaps) : Dialog(blankString, 1, 0, 1) { start_func
    myFrame = NULL;
    modePtr = mode;
    myBrowse = tBrowseArea;
    font = isFont;
    hasMapsPtr = hasMaps;

    Widget* w = NULL;

    if (!isFont) {
        w = new WRadioButton(ID_MODE, getIconSurface(), 0, 160, 20, 20, mode, MODE_EDITTILES);
        w->setToolTip("Edit images");
        w->addTo(this);
        
        w = new WRadioButton(ID_MODE, getIconSurface(), 20, 160, 20, 20, mode, MODE_EDITCOLLISION);
        w->setToolTip("Edit collision maps");
        if (!*hasMaps) w->disable();
        w->addTo(this);
        
        w = new WRadioButton(ID_MODE, getIconSurface(), 40, 160, 20, 20, mode, MODE_EDITASSIGN);
        w->setToolTip("Assign collision maps to images");
        if (!*hasMaps) w->disable();
        w->addTo(this);
    }

    arrangeRow();
    
    runAsPanel();
}

TileSetBrowseToolsMode::~TileSetBrowseToolsMode() { start_func
}

// Browse window- Main

TileSetBrowse::TileSetBrowse(TileSetEdit* myTileset) throw_File : Window() { start_func
    assert(myTileset);
    
    cursor = selectFirst = selectLast = selectBegin = selectEnd = 0;
    haveFocus = partialFocus = 0;
    dirtyRange.w = 0;
    toolsMode = NULL;
    
    tileset = myTileset;
    hasMaps = tileset->getCollisionCount() ? 1 : 0;
    
    // @TODO: These need to be selectable
    zoomRatio = 100;
    gridSize = 1;
    
    // (this setting is purposely not remembered)
    mode = MODE_EDITTILES;

    reloadTileStats();
    
    // @TODO: This needs to be selectable
    showLetters = isFont;

    FrameWindow* myFrame = NULL;
    FrameWindow* toolsModeFrame = NULL;
    
    tileset->markLock(); // exception point
    
    toolsMode = new TileSetBrowseToolsMode(this, isFont, &mode, &hasMaps);

    toolsModeFrame = toolsMode->createWindowed();
    
    myFrame = new FrameWindow(formatString("%s : %s", tileset->getWorld()->getTitle().c_str(), tileset->getName().c_str()), FrameWindow::RESIZING_NORMAL, FrameWindow::FRAMETYPE_BEVEL_BK, this);
    myFrame->addToolPanel(toolsModeFrame, FrameWindow::CLIENT_RIGHT);
    myFrame->show(FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CURRENT, FrameWindow::SHOW_CURRENT);
}

TileSetBrowse::~TileSetBrowse() { start_func
    if (tileset) tileset->markUnlock();
}

void TileSetBrowse::setDirtyRange(int first, int last) { start_func
    if (!numTiles) {
        setDirty(1);
        return;
    }

    assert(first >= 0);
    assert(first < numTiles);
    assert(last >= 0);
    assert(last < numTiles);
    
    if (first > last) swap(first, last);

    // On the same line?
    if (first / tilesPerLine == last / tilesPerLine) {
        setDirtyBox(first, last);
    }
    else {
        // Have to cover entire lines
        setDirtyBox(first - first % tilesPerLine, last - last % tilesPerLine + tilesPerLine - 1);
    }
}

void TileSetBrowse::setDirtyBox(int begin, int end) { start_func
    if (!numTiles) {
        setDirty(1);
        return;
    }

    assert(begin >= 0);
    assert(begin < numTiles);
    assert(end >= 0);
    // ('end' can be greater than number of tiles to complete a box pattern)
    assert(begin <= end);
    assert((begin % tilesPerLine) <= (end % tilesPerLine));

    // Create a rectangle, include outermost gridlines
    Rect range;
    range.x = (begin % tilesPerLine) * (zoomWidth + gridSize);
    range.y = (begin / tilesPerLine) * (zoomHeight + gridSize);
    range.w = (end % tilesPerLine - begin % tilesPerLine + 1) * (zoomWidth + gridSize) + gridSize;
    range.h = (end / tilesPerLine - begin / tilesPerLine + 1) * (zoomHeight + gridSize) + gridSize;
    
    // Add rectangle into dirty range
    boundRects(dirtyRange, range);
    setDirty();
}

void TileSetBrowse::reloadTileStats() { start_func
    numTiles = (mode == MODE_EDITCOLLISION) ? tileset->getCollisionCount() : tileset->getCount();
    tileWidth = tileset->getWidth();
    tileHeight = tileset->getHeight();
    isFont = tileset->getIsFont();
    zoomWidth = tileWidth * zoomRatio / 100;
    zoomHeight = tileHeight * zoomRatio / 100;
    tilesPerLine = tileset->getTilesPerLine();
    fixedTilesPerLine = tilesPerLine ? 1 : 0;
    
    resize(width, height);
    
    // (never put cursor below 0)
    if (numTiles) {
        if (cursor >= numTiles) cursor = numTiles - 1;
        if (selectFirst >= numTiles) selectFirst = numTiles - 1;
        if (selectLast >= numTiles) selectLast = numTiles - 1;
        // This must occur after resize() which sets tilesperline for us if needed
        realignSelection();
    }

    setDirty(1);
}

void TileSetBrowse::resize(int newWidth, int newHeight, int newViewWidth, int newViewHeight, int fromParent) { start_func
    if (newViewWidth == -1) newViewWidth = viewWidth;

    // Calculate tiles per line, if not fixed
    if (!fixedTilesPerLine) {
        // For non fixed tiles, default width is 3/4ths desktop
        if (newViewWidth <= 0) newViewWidth = desktop->desktopWidth() * 3 / 4;
        tilesPerLine = (newViewWidth - gridSize) / (zoomWidth + gridSize);
        if (tilesPerLine < 1) tilesPerLine = 1;
    }

    // Size to match
    int myWidth = tilesPerLine * (zoomWidth + gridSize) + gridSize;
    int myHeight = numTiles / tilesPerLine;
    if (numTiles % tilesPerLine) ++myHeight;
    myHeight = myHeight * (zoomHeight + gridSize) + gridSize;
    
    // Recurse back to parent until we get a match
    if ((myWidth != width) || (myHeight != height)) fromParent = 0;    
    Window::resize(myWidth, myHeight, newViewWidth, newViewHeight, fromParent);
    if (parent) dynamic_cast<FrameWindow*>(parent)->setScroll(zoomWidth + gridSize, zoomHeight + gridSize);
}

int TileSetBrowse::event(int hasFocus, const SDL_Event* event) { start_func
    int drag = 0;
    int key;
    int target;
    int changed = 0;
    ObjChange* obj;

    switch (event->type) {
        case SDL_OBJECTCHANGE:
            obj = (ObjChange*)event->user.data1;
        
            if ((event->user.code & OBJ_TILESET) && (obj->obj == tileset)) {
                if (event->user.code & OBJMOD_DELETE) {
                    tileset = NULL;
                    closeWindow();
                }
                if (event->user.code & OBJMOD_NAME) {
                    if (parent) dynamic_cast<FrameWindow*>(parent)->setTitle(formatString("%s : %s", tileset->getWorld()->getTitle().c_str(), tileset->getName().c_str()));
                }
                if (event->user.code & OBJMOD_COUNTCOLL) {
                    hasMaps = tileset->getCollisionCount() ? 1 : 0;
                    if (!hasMaps) mode = MODE_EDITTILES;
                    toolsMode->updateTools();
                    reloadTileStats();
                }
                if (event->user.code & (OBJMOD_WIDTH | OBJMOD_HEIGHT | OBJMOD_COUNT | OBJMOD_PERLINE)) {
                    reloadTileStats();
                }
                if (event->user.code & OBJMOD_GLYPHW) {
                    setDirtyRange(obj->info1 - 1, obj->info1 - 1);
                }
                if ((event->user.code & OBJMOD_TILE) && (mode != MODE_EDITCOLLISION)) {
                    setDirtyRange(obj->info1 - 1, obj->info2 - 1);
                }
                if (event->user.code & OBJMOD_COLL) {
                    if (mode == MODE_EDITCOLLISION) {
                       setDirtyRange(obj->info1 - 1, obj->info2 - 1);
                    }
                    else if (mode == MODE_EDITASSIGN) {
                       setDirty(1);
                    }
                }
            }
            if ((event->user.code & OBJ_WORLD) && (obj->obj == tileset->getWorld())) {
                if (event->user.code & OBJMOD_DELETE) {
                    tileset = NULL;
                    closeWindow();
                }
                if (event->user.code & OBJMOD_NAME) {
                    if (parent) dynamic_cast<FrameWindow*>(parent)->setTitle(formatString("%s : %s", tileset->getWorld()->getTitle().c_str(), tileset->getName().c_str()));
                }
            }
            return 1;
    
        case SDL_COMMAND:
            if (tileset->getWorldEdit()->commandEvent(event->user.code)) return 1;
            break;
    
        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONDBL:
            if (event->button.button == SDL_BUTTON_LEFT) {
                int mX = event->button.x / (zoomWidth + gridSize);
                int mY = event->button.y / (zoomHeight + gridSize);
                
                // (clip to edge, in cse off of it)
                if (mX >= tilesPerLine) mX = tilesPerLine - 1;
                if (mY >= (numTiles + tilesPerLine - 1) / tilesPerLine) mY = (numTiles + tilesPerLine - 1) / tilesPerLine - 1;
                
                cursorState(mX + mY * tilesPerLine, SDL_GetModState() & KMOD_SHIFT);
                
                if (event->type == SDL_MOUSEBUTTONDBL) {
                    if (mode == MODE_EDITCOLLISION) {
                        tileset->openCollPaintWindow(cursor + 1);
                    }
                    else {
                        tileset->openTilePaintWindow(cursor + 1);
                    }
                }
                return 1;
            }
            break;
    
        case SDL_MOUSEMOTION:
            if (event->motion.state & SDL_BUTTON_LMASK) {
                int mX = ((Sint16)event->motion.x) / (zoomWidth + gridSize);
                int mY = ((Sint16)event->motion.y) / (zoomHeight + gridSize);
                
                // (clip to edge, in case off of it)
                if (mX >= tilesPerLine) mX = tilesPerLine - 1;
                if (mY >= (numTiles + tilesPerLine - 1) / tilesPerLine) mY = (numTiles + tilesPerLine - 1) / tilesPerLine - 1;
                
                cursorState(mX + mY * tilesPerLine, 1);
            }
            return 1;

        case SDL_SPECIAL:
            // Refresh selection rectangle or cursor color?
            if ((event->user.code == SDL_IDLEPHASE) && (partialFocus)) {
                if (fixedTilesPerLine) setDirtyBox(selectBegin, selectEnd);
                else setDirtyRange(selectFirst, selectLast);
            }
            else if ((event->user.code == SDL_IDLECURSOR) && (haveFocus)) {
                setDirtyRange(cursor, cursor);
            }
            return 1;

        case SDL_INPUTFOCUS:
            if (event->user.code & 1) {
                if (!haveFocus) {
                    haveFocus = partialFocus = 1;
                    changed = 1;
                }
            }
            else if (event->user.code & 2) {
                if (!partialFocus) {
                    partialFocus = 1;
                    changed = 1;
                }
            }
            else {
                if (partialFocus) {
                    partialFocus = 0;
                    changed = 1;
                }
            }
            if (!(event->user.code & 1)) {
                if (haveFocus) {
                    haveFocus = 0;
                    changed = 1;
                }
            }
            
            if (changed) {
                if (fixedTilesPerLine) setDirtyBox(selectBegin, selectEnd);
                else setDirtyRange(selectFirst, selectLast);
            }
            return 1;            

        case SDL_KEYDOWN:
            if (event->key.keysym.mod & KMOD_SHIFT) {
                drag = 1; // We'll check this if we care about SHIFT
                key = combineKey(event->key.keysym.sym, event->key.keysym.mod & ~KMOD_SHIFT);
            }
            else {
                key = combineKey(event->key.keysym.sym, event->key.keysym.mod);
            }
        
            switch (key) {
                case SDLK_KP_ENTER:
                case SDLK_RETURN:
                case SDLK_SPACE:
                    tileset->openTilePaintWindow(cursor + 1);
                    return 1;
            
                case SDLK_RIGHT:
                    cursorState(cursor + 1, drag);
                    return 1;
                        
                case combineKey(SDLK_RIGHT, KMOD_CTRL):
                case SDLK_END:
                    cursorState(cursor + tilesPerLine - (cursor % tilesPerLine) - 1, drag);
                    return 1;
                        
                case SDLK_LEFT:
                    if (cursor > 0) cursorState(cursor - 1, drag);
                    return 1;

                case combineKey(SDLK_LEFT, KMOD_CTRL):
                case SDLK_HOME:
                    cursorState(cursor - (cursor % tilesPerLine), drag);
                    return 1;
                        
                case SDLK_DOWN:
                    cursorState(cursor + tilesPerLine, drag);
                    return 1;

                case SDLK_PAGEDOWN:
                    cursorState(cursor + (viewHeight / (zoomHeight + gridSize) - 1) * tilesPerLine, drag);
                    return 1;

                case SDLK_PAGEUP:
                    target = cursor - (viewHeight / (zoomHeight + gridSize) - 1) * tilesPerLine;
                    if (target > 0) cursorState(target, drag);
                    else cursorState(0, drag);
                    return 1;

                case SDLK_UP:
                    if (cursor - tilesPerLine > 0) cursorState(cursor - tilesPerLine, drag);
                    else cursorState(0, drag);
                    return 1;

                case combineKey(SDLK_HOME, KMOD_CTRL):
                    cursorState(0, drag);
                    return 1;

                case combineKey(SDLK_END, KMOD_CTRL):
                    cursorState(numTiles - 1, drag);
                    return 1;
            }
            break;
    }
    return 0;
}

void TileSetBrowse::realignSelection() { start_func
    if (selectFirst % tilesPerLine <= selectLast % tilesPerLine) {
        selectBegin = selectFirst;
        selectEnd = selectLast;
    }
    else {
        selectBegin = selectFirst - selectFirst % tilesPerLine + selectLast % tilesPerLine;
        selectEnd = selectLast - selectLast % tilesPerLine + selectFirst % tilesPerLine;
    }
}

int TileSetBrowse::cursorState(int newPos, int drag) { start_func
    if (newPos >= 0) {
        // Clip on right
        if (newPos >= numTiles) newPos = numTiles - 1;
        
        // Dirty previous selection
        if (fixedTilesPerLine) setDirtyBox(selectBegin, selectEnd);
        else setDirtyRange(selectFirst, selectLast);

        // Drag?
        if (drag) {
            // If selection beginning matches cursor
            if (selectFirst == cursor) {
                // Drag beginning point
                selectFirst = cursor = newPos;
            }
            else {
                // Drag end point
                selectLast = cursor = newPos;
            }            
            
            // Ensure in proper order
            if (selectLast < selectFirst) {
                swap(selectFirst, selectLast);
            }
        }
        else {
            // Reset selection and set cursor
            selectFirst = selectLast = cursor = newPos;
        }

        // Scroll?
        int newY = cursor / tilesPerLine * (zoomHeight + gridSize);
        int newX = (cursor % tilesPerLine) * (zoomWidth + gridSize);
        if (parent) dynamic_cast<FrameWindow*>(parent)->scrollToView(newX, newY, zoomWidth + gridSize * 2, zoomHeight + gridSize * 2);
        realignSelection();

        // Dirty
        if (fixedTilesPerLine) setDirtyBox(selectBegin, selectEnd);
        else setDirtyRange(selectFirst, selectLast);
    }
    
    return cursor;
}

void TileSetBrowse::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
    assert(destSurface);

    if (visible) {
        // If dirty, redraw range or all
        if (dirty) {
            if (totalDirty) {
                getRect(toDisplay);
                toDisplay.x += xOffset;
                toDisplay.y += yOffset;
            }
            else {
                dirtyRange.x += x + xOffset;
                dirtyRange.y += y + yOffset;
                // Range must include requested update area as well
                boundRects(toDisplay, dirtyRange);
            }
            dirty = totalDirty = 0;
            dirtyRange.w = 0;
            intersectRects(toDisplay, clipArea);
        }
        
        xOffset += x;
        yOffset += y;
        
        // Anything to draw?
        if (toDisplay.w) {
            SDL_SetClipRect(destSurface, &toDisplay);
            SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_BKFILL]);

            int dX = xOffset + gridSize;
            int lastX = xOffset + width;
            
            // Draw starting at the first line of the displayed area;
            int skip = (toDisplay.y - yOffset) / (zoomHeight + gridSize);

            // Make sure this includes the tile above a gridline
            // if that gridline is the very top pixel being shown
            if (((toDisplay.y - yOffset) % (zoomHeight + gridSize) == 0) && (skip > 0)) --skip;

            int dY = yOffset + skip * (zoomHeight + gridSize) + gridSize;
            int lastY = toDisplay.y + toDisplay.h;
            int pos = tilesPerLine * skip;

            // Determine first/last tiles of each row we need to show
            // These numbers would be, for example, 0 for first tile, 1 for second,
            // up to tilesPerLine - 1
            int leftmost = (toDisplay.x - xOffset) / (zoomWidth + gridSize);
            int rightmost = (toDisplay.x + toDisplay.w - xOffset - 1) / (zoomWidth + gridSize);
            
            // Make sure this includes the tile to the left of a gridline
            // if that gridline is the very left pixel being shown
            if (((toDisplay.x - xOffset) % (zoomWidth + gridSize) == 0) && (leftmost > 0)) --leftmost;
            
            pos += leftmost;
            dX += (zoomWidth + gridSize) * leftmost;
            
            int selectBeginMod = selectBegin % tilesPerLine;
            int selectEndMod = selectEnd % tilesPerLine;
            
            for (; (pos < numTiles) && (dY < lastY); ++pos) {
                int posMod = pos % tilesPerLine;
            
                // We use modulo to determine if this tile needs displaying
                if ((posMod >= leftmost) && (posMod <= rightmost)) {
                    // Draw tile @TODO: doesn't zoom
                    if (mode == MODE_EDITCOLLISION) {
                        tileset->blitCollFx(pos + 1, destSurface, dX, dY, 0, 0, 0, 0, 0xffffffff);
                    }
                    else {
                        tileset->blitTile(pos + 1, destSurface, dX, dY);
                    }
                    
                    // Glyph width
                    if (isFont) {
                        int glyphWidth = tileset->getGlyphWidth(pos + 1);
                        glyphWidth = glyphWidth * zoomRatio / 100;
                        drawVLine(dY, dY + zoomHeight, glyphWidth + dX, guiPacked[COLOR_GLYPHWIDTH], destSurface);
                        
                        if (showLetters) {
                            string glyph(1, pos + 32);
                            drawText(glyph, guiRGB[COLOR_TOOLTIP], dX + zoomWidth - fontWidth(glyph), dY + zoomHeight - fontHeight(), destSurface);
                        }
                    }
                    
                    // Overlay collision data in assign mode @TODO
                    
                    if (partialFocus) {
                        // Selected or cursor- if fixed width, within begin/end
                        // If not, within first/last                
                        if (((fixedTilesPerLine) &&
                             (pos >= selectBegin) && (pos <= selectEnd) &&
                             (posMod >= selectBeginMod) &&
                             (posMod <= selectEndMod))
                            ||
                            ((!fixedTilesPerLine) &&
                             (pos >= selectFirst) && (pos <= selectLast))
                           ) {
                            drawSelectRect(dX, dY, zoomWidth, zoomHeight,
                                           guiPacked[(pos == cursor) && (haveFocus) ? COLOR_TILECURSOR : COLOR_TILESELECTION], destSurface,
                                           (pos == cursor) && (haveFocus) ? desktop->currentCursorAlpha() : 128);
                                           
                            if (pos == cursor) {
                                drawBox(dX - gridSize, dY - gridSize, zoomWidth + gridSize * 2, zoomHeight + gridSize * 2, guiPacked[COLOR_TILECURSORBORDER1], destSurface);
                                drawBox(dX - gridSize + 1, dY - gridSize + 1, zoomWidth + gridSize * 2 - 2, zoomHeight + gridSize * 2 - 2, guiPacked[COLOR_TILECURSORBORDER2], destSurface);
                            }
    
                            // Draw ants borders
                            if (selectFirst != selectLast) {
                                int drawT = 1;
                                int drawB = 1;
                                int drawL = 1;
                                int drawR = 1;
                            
                                if (fixedTilesPerLine) {
                                    if (posMod > selectBeginMod) drawL = 0;
                                    if (posMod < selectEndMod) drawR = 0;
                                    if (pos - selectBegin >= tilesPerLine) drawT = 0;
                                    if (selectEnd - pos >= tilesPerLine) drawB = 0;
                                }
                                else {
                                    if (pos > selectFirst) drawL = 0;
                                    if (pos < selectLast) drawR = 0;
                                    if (pos - selectFirst >= tilesPerLine) drawT = 0;
                                    if (selectLast - pos >= tilesPerLine) drawB = 0;
                                }
                                
                                if (drawT) drawAntsHLine(dX - gridSize, dX + zoomWidth - 1 + gridSize, dY - gridSize, desktop->currentSelectionPhase(), destSurface);
                                if (drawB) drawAntsHLine(dX - gridSize, dX + zoomWidth - 1 + gridSize, dY + zoomHeight - 1 + gridSize, desktop->currentSelectionPhase(), destSurface);
                                if (drawL) drawAntsVLine(dY - gridSize, dY + zoomHeight - 1 + gridSize, dX - gridSize, desktop->currentSelectionPhase(), destSurface);
                                if (drawR) drawAntsVLine(dY - gridSize, dY + zoomHeight - 1 + gridSize, dX + zoomWidth - 1 + gridSize, desktop->currentSelectionPhase(), destSurface);
                            }
                        }
                    }
                }
                    
                dX += zoomWidth + gridSize;
                if (dX + zoomWidth > lastX) {
                    dX = xOffset + gridSize;
                    dY += zoomHeight + gridSize;
                }
            }
        }
    }
}

// @TODO: select all/deselct/cutcopypaste/zoom/grid
Window::CommandSupport TileSetBrowse::supportsCommand(int code) const { start_func
    switch (code) {
        // @TODO: these 3 should be radios
        case MODE_EDITTILES:
            return isFont ? Window::COMMAND_HIDE : Window::COMMAND_ENABLE;
            
        case MODE_EDITCOLLISION:
        case MODE_EDITASSIGN:
            return isFont ? Window::COMMAND_HIDE : (hasMaps ? Window::COMMAND_ENABLE : Window::COMMAND_DISABLE);
    }
    
    return tileset->getWorldEdit()->supportsCommand(code);
}

Window::WindowType TileSetBrowse::windowType() const { start_func
    return WINDOW_CLIENT;
}

